
#ifndef __ENGINE_PACKAGE_included
#define __ENGINE_PACKAGE_included

#include <map>
#include <string>
#include <ostream>
#include <istream>
#include <exception>
#include <boost/exception/all.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/any.hpp>
#include <boost/lexical_cast.hpp>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include "DebugStream.h"

using namespace boost;
using namespace std;

#define PROTOCOL_TAG "MSP/1.0"

#define PACKAGE_PROP_DEVICE_TYPE "Device-Type"
#define PACKAGE_PROP_SESSION "Session"
#define PACKAGE_PROP_SET_SESSION "Set-Session"
#define PACKAGE_PROP_DEVICE "Device"
#define PACKAGE_PROP_TOKEN "Token"
#define PACKAGE_PROP_SET_TOKEN "Set-Token"
#define PACKAGE_PROP_PARAMETERS "Parameters"
#define PACKAGE_PROP_HOST_IP "Host-IP"
#define PACKAGE_PROP_SERV_IP "Serv-IP"
#define PACKAGE_PROP_AUTHENTICATE "WWW-Authenticate"
#define PACKAGE_PROP_AUTHORIZATION "Authorization"
#define PACKAGE_PROP_MAGIC "Magic"
#define PACKAGE_PROP_CONTENT_TYPE "Content-Type"
#define PACKAGE_PROP_CONTENT_LENGTH "Content-Length"

#define PACKAGE_CONTENT_TYPE_JSON "application/json"
#define PACKAGE_CONTENT_TYPE_RAW "application/octet-stream"

namespace Engine {

class CPackageParseException : public virtual std::exception, public virtual boost::exception {};
typedef boost::error_info<struct tag_parse_error, string> parse_error;

class CSession;
class CDevice;

class CPackage
{
protected :

	boost::any mAnyData;
	std::map<string, string> mProperties;
	boost::asio::streambuf mContent;
	string mContentFileName;
	std::ios::pos_type mContentSize;

	boost::shared_ptr<std::istream> mContentInputStream;
	boost::shared_ptr<CDevice> mDevice;

	void ParseProperties(std::istream& is);

public:

	typedef rapidjson::Document JsonDocument;
	typedef rapidjson::Value JsonValue;
	typedef rapidjson::StringBuffer JsonBuffer;
	typedef rapidjson::Writer<rapidjson::StringBuffer> JsonWriter;

	CPackage(void);
	CPackage(boost::asio::streambuf& data);
	CPackage(boost::shared_ptr<std::istream> is);

	virtual ~CPackage(void);

	void SetAnyData(boost::any data) { mAnyData = data; }

	template<typename ValueType>
	ValueType GetAnyData() { return any_cast<ValueType>(mAnyData); }

	void AddPropertie(string& name, string& value) { mProperties[name] = value; }
	void DelPropertie(string& name) { mProperties.erase(name); }
	string& GetPropertie(string& name) {
		map<string, string>::iterator i = mProperties.find(name);
		if(i != mProperties.end()) return i->second;
		else return NULL_STRING;
	}

	int GetPropertieInt(string& name) {
		string& value = GetPropertie(name);
		//DebugStream << "GetPropertieInt() : " << name << '=' << value << '\n';
		if(value.length() == 0) return 0;
		try {
			return boost::lexical_cast<int>(value);
		}
		catch(std::exception&) {
			return 0;
		}
	}

	void SetContent(string& content, string& type);
	void SetContentFile(string& filename, string& type);

	boost::shared_ptr<ostream> GetContentOutputStream() {
		return boost::shared_ptr<ostream>(new ostream(&mContent)); 
	}
	
	boost::shared_ptr<istream> GetContentInputStream();
	void GetContentAsString(string& s);
	boost::shared_ptr<JsonDocument> ParseContentAsJson();
	bool SaveContent(const string& file_path);
	void SkipContent();

	virtual void GetData(ostream& os);
	virtual bool IsRequest() = 0;

	void SetDevice(boost::shared_ptr<CDevice> dev) { mDevice = dev; }
	void ResetDevice() { mDevice.reset(); }
	boost::shared_ptr<CDevice> GetDevice() { return mDevice; }

	static string NULL_STRING;
};

typedef boost::shared_ptr<CPackage> PackageRef;

}
#endif //__ENGINE_PACKAGE_included
